package montaltoController
import montaltoNeds._
import montaltoFood._
import montaltoDirections._
import montaltoGrids._
import scala.collection.mutable.Stack
import scala.collection.mutable.Queue
import montaltoUtils.RandomSingleton
/**
 * Created by IntelliJ IDEA.
 * User: Andrew Montalto
 * Date: Apr 27, 2010
 * Time: 9:35:29 PM
 * To change this template use File | Settings | File Templates.
 */

/**
 * Aggregates ned controls
 */

/**
 * This is the controller of the commands
 * 
 */
class SimController{
    val commands:Queue[AbstractCommand] = new Queue
    def addCommands(cmd:AbstractCommand)={
      commands.enqueue(cmd)
    }
    def executeCommands()={
      commands.foreach{
        cmd:AbstractCommand => cmd.execute()
      }
    }
}
//command design pattern
abstract class AbstractCommand(grid:Grid) {
  var myGrid:Grid=grid
  var testing=false
  var myIterator = GridIteratorCreator.create(myGrid).asInstanceOf[GridIterator]
  def execute()
}

class GrowFoodCommand(grid:Grid) extends AbstractCommand(grid:Grid) {

  override def execute()={
    //Iterator pattern used based on the java pattern
     myIterator.first
    while(myIterator.hasNext){
      myIterator.current().growFood()
      myIterator.next()
    }
  }
}

class NedsEat(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()= {
    myIterator.first
    while(myIterator.hasNext){
      myIterator.current().neds.foreach{
        n:Ned=>
            n.eat
            n.executedCommand=false
            n.hasMated=false
          }
       myIterator.next
    }
    }
  }

class NedsAddFood(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute() = {
    myIterator.first
    while (myIterator.hasNext){
      myIterator.current().neds.foreach{n:Ned=>
        n.bag.add(myIterator.current().getTopFood)
      }
      myIterator.next
    }
  }
}

class NedsAge(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()={
    myIterator.first
    while(myIterator.hasNext){
      myIterator.current().neds.foreach{n:Ned=>n.getOlder}
      myIterator.next
    }
  }
}

class MoveToNedCommand(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()={
    myIterator.first
    while(myIterator.hasNext){
      myIterator.current().neds.foreach{n:Ned=>n.moveToNextCommand}
      myIterator.next
    }
  }
}

class TurnCommand(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute ()={
    myIterator.first
    while(myIterator.hasNext){
      myIterator.current().neds.foreach{
        n:Ned=> if ((n.currentCommandString=="turn"||testing)
                && n.executedCommand==false){
          n.turn
        }
      }
       myIterator.next
    }
  }
}

class MoveCommand(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()={
    myIterator.first
    while(myIterator.hasNext){
      var cnt:Int = 0;
      var remStack = new Stack[Int]
      myIterator.current().neds.foreach{
        n:Ned=>if((n.currentCommandSubStr=="move"|| testing)&& n.executedCommand==false ){
          var d:Direction = DirectionList.get(n.direction)
          var currentX = myIterator.current().coodinate.x
          var currentY = myIterator.current().coodinate.y
          if(!nextIsBoundry(currentX + d.getX,currentY + d.getY))
            {
                remStack.push(cnt)//push the index of ned to remove on stack
                n.executedCommand=true;
                grid.gridArray(currentX + d.getX)(currentY + d.getY).settleNed(n)
            }
        }

        cnt = cnt + 1

      }
       //remove the moved neds
       while(!remStack.isEmpty) {
        myIterator.current().removeNed(remStack.pop)
       }
       myIterator.next
    }
  }
  
  def nextIsBoundry(nextX:int, nextY:int):Boolean={
     if(nextX < 0 || nextX >= myGrid.gridArray.indices.length) {
       return true
     }else if(nextY < 0 || nextY >= myGrid.gridArray.indices.length){
       return true
     }else {
       return false;
     }
  }
}

class MateNeds (grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()={
    myIterator.first
     var newNeds:Stack[Ned] = new Stack()
     while (myIterator.hasNext){
        var nedStack:Stack[Ned] = new Stack()
        myIterator.current().neds.foreach{n:Ned =>
         nedStack.push(n)
        }

       myIterator.current().neds.foreach{n:Ned =>
           if(n.currentCommandSubStr=="mate"|| testing){
             nedStack.foreach{target:Ned =>
               val mateCmd:Mate = new Mate(n,target)
               if(mateCmd.tryMating){ 
                 newNeds.push(mateCmd.babyNed)
               }
             }
           }
       }
       //add the baby neds to the simulator
       while(!newNeds.isEmpty){
         myIterator.current().settleNed(newNeds.pop)
       }
       myIterator.next
     }
  }
  
}
class CleanDeadNeds(grid:Grid) extends AbstractCommand(grid:Grid){
  override def execute()={
      myIterator.first
      var deadStack = new Stack[Ned]
      var deadInt = new Stack[Int]

      while(myIterator.hasNext){
        var cnt:Int = 0;
         myIterator.current().neds.foreach{n:Ned=>

         if(!n.isAlive){
           deadInt.push(cnt)

         }
       }
        while(!deadInt.isEmpty){
        myIterator.current().removeNed(deadInt.pop)
      }
        myIterator.next()
        cnt = cnt+1
      }

  }

}
class AddTurnToGrid(grid:Grid) extends AbstractCommand(grid:Grid){
    
    override def execute()={
      grid.turn = grid.turn + 1
    }
}

//if there is a ned of the same sex in the grid
//Some thing like a mediator
//they produce a new ned
//each ned consumes two food
class Mate(n1:Ned, n2:Ned){
   val _n1 = n1;
   val _n2 = n2;
   var babyNed:Ned=null
   val splitIndex   = RandomSingleton.INSTANCE.nextPosInt(40)
   def tryMating:Boolean={
       if(n1.hasMated || n1.bag.currentCalories < 2){
         return false

       }

       if(n2.hasMated || n2.bag.currentCalories < 2){
          return false
       }

       if((_n1.gender.isInstanceOf[Male] && _n2.gender.isInstanceOf[Female]) ||
          (_n1.gender.isInstanceOf[Female] && _n2.gender.isInstanceOf[Male])){
         babyNed = new Ned(splitCode(_n1.geneticCode,_n2.geneticCode))
         babyNed.hasMated = true;//so they don't mate immediately
         n1.hasMated = true
         n2.hasMated = true
         n1.mates = n1.mates + 1
         n2.mates = n2.mates + 1
         n1.bag.use(2)
         n2.bag.use(2)
         return true
       }else{
         return false
       }
   }

   def splitCode(gen1:GeneticCode, gen2:GeneticCode):GeneticCode={
     var gc= new GeneticCode
     val s1 = gen1.theCode.substring(0,splitIndex)
     val s2 = gen2.theCode.substring(splitIndex, gen2.theCode.length)
     gc.custom(s1.concat(s2) )
     return gc
   }

}



